/**
 WhereIsMyMail Copyright (c) 2012 Artem Kuroptev

 This file is part of WhereIsMyMail. WhereIsMyMail is free software: you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the Free Software Foundation,
 either version 3 of the License, or (at your option) any later version.

 WhereIsMyMail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with WhereIsMyMail. If not, see <http://www.gnu.org/licenses/>.
 */

package com.ucware.wimm;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

/**
 * $Id: AbstractUp.java 24 2014-11-25 21:06:27Z kuroptev@gmail.com $
 * @created 01.12.2012
 * @author Artem Kuroptev
 */
public class AbstractUp
{
    private static final Logger log = Logger.getLogger(AbstractUp.class.getName());
    public static final SimpleDateFormat ATOM_DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    protected static void log(Object s) {log.info(s.toString());}
    protected static void log(Throwable s) {log.log(Level.SEVERE, s.getLocalizedMessage(), s);}

    protected static final int SUBJ_IN_FILENAME_MAX_LENGTH = 144/2;

    public static final String FILE_ENCODING = System.getProperty("file.encoding");
    public static final char FIELDSEP = '-';
    public static final char SP = '_';


    public static void setupLogManager() throws IOException {
        if(System.getProperty("java.util.logging.config.file") != null) {
            System.err.println("using java.util.logging.config.file");
            return;
        } else {

            final InputStream is = AbstractUp.class.getResourceAsStream("logging.properties");
            if(is == null) {
                throw new RuntimeException("AbstractUp.class.getResourceAsStream(\"logging.properties\") returned null");
            }
            try {
                LogManager.getLogManager().readConfiguration(is);
            }
            finally {
                is.close();
            }
        }
    }

    public static void exportMessages(final Message[] messages) throws MessagingException, IOException
    {
        log("Messages to save: " + messages.length);
        log("Saving messages to files.. ");

        for (int i = 0; i < messages.length; i++)
        {
            final Message m = messages[i];
            if (m instanceof MimeMessage) {
                final MimeMessage mm = (MimeMessage) m;
                final String baseName = createFileNameBase(mm, System.currentTimeMillis());

                File f = new File(baseName+".msg");
                while(f.exists()) {
                    f = new File(baseName+"."+System.currentTimeMillis()+".msg");
                }

                log("Message: "+(1+i)+"/"+ messages.length + ", File: "+ f.getAbsolutePath());
                final OutputStream out = new FileOutputStream(f);
                try {
                    mm.writeTo(out);
                }
                finally {
                    out.close();
                }
            }
            else {
                throw new IllegalStateException("Unexpected Message class: "+ m.getClass().getName());
            }
        }
        
        log("exportMessages(..) done at: " + new Date());
    }

    public static String createFileNameBase(MimeMessage msg, long received) throws MessagingException
    {
        final StringBuffer name = new StringBuffer();

        // RECEIVED
        name.append(received);

        // SENT
        Date sent;
        try {
            sent = msg.getSentDate();
        }
        catch (MessagingException e) {
            sent = new Date(0);
            log.warning("'sent' is 'null' becouse of: " + e.toString());
        }
        sent = sent !=null ? sent : new Date(0);
        name.append(FIELDSEP).append(sent.getTime());
        name.append(FIELDSEP).append(MessageFormat.format("{0,date,yyyy"+SP+"MM"+SP+"dd"+FIELDSEP+"HH"+SP+"MM}", sent));

        // FROM
        String from;
        Address[] addresses;
        try {
            addresses = msg.getFrom();
        }
        catch (Exception e) // javax.mail.MessagingException, javax.mail.internet.AddressException
        {
            addresses = null;
            log.warning("'addresses' & 'from' are 'null' becouse of: " + e.toString());
        }
        if(addresses == null || addresses.length == 0) {
            from = String.valueOf(addresses);
        }
        else {
            if(addresses[0] instanceof InternetAddress) {
                final InternetAddress internetAddress = (InternetAddress) addresses[0];
                from = internetAddress.getAddress();
            }
            else {
                from = addresses[0].toString();
            }
        }
        from = from.replace('\\',FIELDSEP).replace('/',FIELDSEP).replace('+',FIELDSEP);
        name.append(FIELDSEP).append(from);

        // SUBJ
        String rawSubj;
        try {
            rawSubj = msg.getSubject();
        }
        catch (MessagingException e) {
            rawSubj = null;
            log.warning("'subj' is 'null' becouse of: " + e.toString());
        }
        final String subj = rawSubj != null ? rawSubj : String.valueOf(rawSubj);
        final String subjNorm = normalize(subj, SUBJ_IN_FILENAME_MAX_LENGTH);
        if(subjNorm.length() > 0) {
            name.append(FIELDSEP).append(subjNorm);
        }

        // MSGID
        String messageID;
        try {
            messageID = msg.getMessageID();
        }
        catch (MessagingException e) {
            messageID = null;
            log.warning("'messageID' is 'null' becouse of: " + e.toString());
        }
        if(messageID!=null) {
            name.append(FIELDSEP).append("0x").append(Integer.toOctalString(messageID.hashCode()));
        }

        return name.toString();
    }

    public static String normalize(String s, int maxLength) {

        final StringBuffer out = new StringBuffer(s.length());
        for (int i = 0; (i < s.length() && i<maxLength); i++)
        {
            final char c = s.charAt(i);
            if (Character.isLetterOrDigit(c)
                    && !(c>127 && c<256) // unencoded (bad mailers) data in subj, etc
                    && nativeEncodingAllows(c)) {
                out.append(c);
            }
            else {
                if (out.length() > 0 && out.charAt(out.length() - 1) != SP) {
                    out.append(SP);
                }
            }
        }
        if(out.length() > 0 && out.charAt(out.length() - 1) == SP) {
            out.deleteCharAt(out.length() - 1);
        }
        return out.toString();
    }

    public static boolean nativeEncodingAllows(char c) {
        try {
            final String s = new String(String.valueOf(c).getBytes(FILE_ENCODING), FILE_ENCODING);
            return s.length() > 0 && s.charAt(0) == c;
        }
        catch (UnsupportedEncodingException e){
            log.warning(e.toString());
            return false;
        }
    }

    public static Properties readProperties(String fn) throws IOException
    {
        final File inPropFile = new File(fn);
        if(inPropFile.exists() && inPropFile.isFile()) {
            final FileInputStream fos = new FileInputStream(inPropFile);
            try {
                final Properties inProp = new Properties();
                inProp.load(fos);
                return inProp;
            }
            finally {
                fos.close();
            }
        }
        else {
            throw new RuntimeException("File not found: " + inPropFile);
        }
    }

    public static void writeProperties(String fn, Properties inProp) throws IOException
    {
        final File inPropFile = new File(fn);
        final FileOutputStream fos = new FileOutputStream(inPropFile);
        try {
            inProp.store(fos, "http://code.google.com/p/where-is-my-mail/ Rev.$Revision: 24 $");
        }
        finally {
            fos.close();
        }
    }

    public static Date getDatePropertyOrNull(Properties properties, String name) throws ParseException
    {
        final String afterProp = properties.getProperty(name);
        if(afterProp != null) {
            return ATOM_DATETIME_FORMAT.parse(afterProp);
        }
        else {
            return null;
        }
    }
}
